/**
 * Copyright 2008 Hotye Inc.
 */
package craneding.gwt.databean.client.ui;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Widget;

import craneding.gwt.databean.client.model.DynamicBean;
import craneding.gwt.databean.client.model.FieldInfo;
import craneding.gwt.databean.client.model.FieldInfoX;
import craneding.gwt.databean.client.model.FieldInfoXes;
import craneding.gwt.databean.client.model.FieldInfo.UI;
import craneding.gwt.databean.client.ui.field.CalendarFieldInput;
import craneding.gwt.databean.client.ui.field.IFieldInput;
import craneding.gwt.databean.client.ui.field.ImageFieldInput;
import craneding.gwt.databean.client.ui.field.NumberFieldInput;
import craneding.gwt.databean.client.ui.field.PasswordFieldInput;
import craneding.gwt.databean.client.ui.field.SelectFieldInput;
import craneding.gwt.databean.client.ui.field.TextAreaFieldInput;
import craneding.gwt.databean.client.ui.field.TextFieldInput;
import craneding.gwt.databean.client.ui.field.ValidationException;

/**
 * Simple Bean Form
 * 
 * @author crane.ding
 * @version 1.0 2008-12-1
 */
public class SimpleBeanForm extends Composite {

	private final FlexTable table = new FlexTable();
	private DynamicBean dynamicBean;
	private int gridColumns = 5;
	private String[] fields;
	@SuppressWarnings("unused")
	private String[] categories;
	private Widget[] userWidgets;

	private Map<String, Integer> fieldSpans = new HashMap<String, Integer>();
	private Map<String, IFieldInput> fieldUIs = new HashMap<String, IFieldInput>();

	public SimpleBeanForm() {
		initWidget(table);
		setStyleName("SimpleBeanForm");
	}

	public SimpleBeanForm(DynamicBean dynamicBean) {
		this();
		setDynamicBean(dynamicBean);
	}

	/**
	 * set grid col's span
	 * 
	 * @param gridColumns
	 *            grid col's span
	 * @return {@link SimpleBeanForm}
	 */
	public SimpleBeanForm setGridColumns(int gridColumns) {
		this.gridColumns = gridColumns;
		return this;
	}

	public DynamicBean getDynamicBean() {
		return dynamicBean;
	}

	/**
	 * <h3>set dynamicBean and show the ui into the table.</h3>
	 * <ul>
	 * <li>after setFieldOrder method</li>
	 * <li>after setCategoryOrder method</li>
	 * <li>after setGridColumns method</li>
	 * <li>after setFieldSpans method</li>
	 * </ul>
	 * 
	 * @param dynamicBean
	 */
	public void setDynamicBean(DynamicBean dynamicBean) {
		this.dynamicBean = dynamicBean;
		loadDynamicBeanInfo();
	}

	/**
	 * set field display order
	 * 
	 * @param fields
	 *            field's order
	 * @return {@link SimpleBeanForm}
	 */
	public SimpleBeanForm setFieldOrder(String... fields) {
		this.fields = fields;
		return this;
	}

	public SimpleBeanForm setCategoryOrder(String... categories) {
		this.categories = categories;
		return this;
	}

	/**
	 * set field's col span
	 * 
	 * @param field
	 *            field name
	 * @param columns
	 * @return {@link SimpleBeanForm}
	 */
	public SimpleBeanForm setFieldSpans(String field, int columns) {
		fieldSpans.put(field, columns);
		return this;
	}

	/**
	 * set user Widget
	 * 
	 * @param userWidgets
	 * @return {@link SimpleBeanForm}
	 */
	public SimpleBeanForm setUserWidgets(Widget... userWidgets) {
		this.userWidgets = userWidgets;
		return this;
	}

	public boolean validityCheck() {
		List<Object> values = new ArrayList<Object>();
		for (String field : fields) {
			IFieldInput fieldInput = fieldUIs.get(field);
			try {
				fieldInput.validityCheck();
				values.add(fieldInput.getValue());
			} catch (ValidationException e) {
				String message = e.getMessage();
				Window.alert(message);
				return false;
			}
		}
		int index = 0;
		for (String field : fields) {
			dynamicBean.setField(field, values.get(index++));
		}
		return true;
	}

	private void loadDynamicBeanInfo() {
		if (fields == null)
			fields = dynamicBean.getFieldNames();
		// remove all row.
		int rowCount = table.getRowCount();
		for (int i = 0; i < rowCount; i++) {
			table.removeRow(0);
		}
		// clear all old UI
		fieldUIs.clear();
		int row = 0, column = 0;
		for (String field : fields) {
			// change row.
			if ((column / 2) >= gridColumns) {
				row++;
				column = 0;
			}
			FieldInfo fieldInfo = dynamicBean.getFieldInfo(field);
			IFieldInput widget = createFieldInput(fieldInfo.ui());
			// put widget into fieldUIs's Map.
			fieldUIs.put(field, widget);
			// set widget's info.
			widget.setFieldInfo(field, fieldInfo);
			widget.setValue(dynamicBean.getField(field));
			// set widget into table.
			StringBuffer str = new StringBuffer();
			if (fieldInfo.icon().length() > 0)
				str.append("<img src='").append(fieldInfo.icon()).append("'/>");
			str.append(fieldInfo.label());
			table.setWidget(row, column++, new HTML(str.toString()));
			table.setWidget(row, column++, (Widget) widget);
			// set FieldInfox's value into widget.
			FieldInfoXes infoXes = dynamicBean.getFieldInfoXes(field);
			if (infoXes != null) {
				for (FieldInfoX infoX : infoXes.value()) {
					Element elem = widget.getElement();
					DOM.setElementProperty(elem, infoX.name(), infoX.value());
				}
			}
			// set field spans.
			Integer spans = fieldSpans.get(field);
			if (spans != null) {
				table.getFlexCellFormatter().setColSpan(row, column - 1,
						spans * 2 - 1);
			} else if (widget instanceof TextAreaFieldInput) {
				// textareFieldInput default view a row.
				table.getFlexCellFormatter().setColSpan(row, column - 1,
						gridColumns * 2 - 1);
				row++;
				column = 0;
			}
		}
		loadUserWidgets();
	}

	private IFieldInput createFieldInput(UI ui) {
		switch (ui) {
		case text:
			return new TextFieldInput();
		case numbertext:
			return new NumberFieldInput();
		case select:
			return new SelectFieldInput();
		case textarea:
			return new TextAreaFieldInput();
		case calendar:
			return new CalendarFieldInput();
		case password:
			return new PasswordFieldInput();
		case radio:
			return new TextFieldInput();
		case checkbox:
			return new TextFieldInput();
		case image:
			return new ImageFieldInput();
		case file:
			return new TextFieldInput();
		case hidden:
			return new TextFieldInput();
		default:
			return new TextFieldInput();
		}
	}

	private void loadUserWidgets() {
		if (userWidgets == null)
			return;
		HorizontalPanel hor = new HorizontalPanel();
		for (Widget w : userWidgets) {
			hor.add(w);
		}
		int rowCount = table.getRowCount();
		table.setWidget(rowCount, 0, hor);
		table.getFlexCellFormatter().setColSpan(rowCount, 0,
				gridColumns * 2 - 1);
	}

}
